/**
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 *   
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 */
package org.jiopi.ibean.kernel.context.classloader;

import org.jiopi.ibean.kernel.KernelConstants;
import org.jiopi.ibean.kernel.NameVersion;
import org.jiopi.ibean.kernel.context.ContextCentralConsoleKernel;
import org.jiopi.ibean.kernel.repository.ModuleResource;
import org.jiopi.ibean.kernel.repository.Repository;
import org.jiopi.ibean.kernel.util.FileUtil;

import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;


/**
 * 
 * Blueprint Class Loader Manager
 * <P>
 * Cache specified Blueprint Class Loader <br/>
 * 
 * </P>
 * 
 * @since 2010.5.14
 *
 */
public class BlueprintClassLoaderManager {
	
	private final ContextCentralConsoleKernel ccck;
	
	private final Map<NameVersion,WeakReference<IBeanClassLoader>> blueprintClassLoaders;
	
	private final Map<NameVersion,WeakReference<IBeanClassLoader>> realBlueprintClassLoaders;
	
	public BlueprintClassLoaderManager(ContextCentralConsoleKernel ccck){
		this.ccck = ccck;
		blueprintClassLoaders = new HashMap<NameVersion,WeakReference<IBeanClassLoader>>();
		realBlueprintClassLoaders = new HashMap<NameVersion,WeakReference<IBeanClassLoader>>();
	}
	
	public IBeanClassLoader getBlueprintClassLoader(String blueprintName,String compatibleVersion){
		if(blueprintName == null) return null;
		NameVersion needNameVersion = new NameVersion(blueprintName,compatibleVersion);
		return getBlueprintClassLoader(needNameVersion);
	}
	
	public IBeanClassLoader getBlueprintClassLoader(NameVersion needNameVersion){
		IBeanClassLoader blueprintClassLoader = null;
		synchronized(blueprintClassLoaders){
			WeakReference<IBeanClassLoader> ref = blueprintClassLoaders.get(needNameVersion);
			if(ref!=null) blueprintClassLoader=ref.get();
		}
		
		if(blueprintClassLoader == null){
			synchronized(("bp"+needNameVersion.id).intern()){
				ModuleResource mr = Repository.getModuleResource(needNameVersion,Repository.BLUEPRINT);
				
				if(mr == null) throw new RuntimeException("Blueprint of "+needNameVersion.name+(needNameVersion.version==null?"":needNameVersion.version+" ")+" is not found");
				NameVersion realNameVersion = new NameVersion(mr.name,mr.version);
				
				synchronized(realBlueprintClassLoaders){
					WeakReference<IBeanClassLoader> ref = realBlueprintClassLoaders.get(realNameVersion);
					if(ref!=null) blueprintClassLoader=ref.get();
				}
				
				if(blueprintClassLoader == null){//not initiated 
					synchronized(("bp"+mr.id).intern()){
						synchronized(realBlueprintClassLoaders){
							WeakReference<IBeanClassLoader> ref = realBlueprintClassLoaders.get(realNameVersion);
							if(ref!=null) blueprintClassLoader=ref.get();
						}
						if(blueprintClassLoader == null){
							String moduleTempDir = FileUtil.joinPath(ccck.contextDir.getAbsolutePath(),KernelConstants.TMP_DIR,mr.id);
							String moduleDir = FileUtil.joinPath(ccck.contextDir.getAbsolutePath(),mr.id);
							NameVersion blueprintNameVersion = new NameVersion(needNameVersion.name,mr.version);
							IBeanClassLoader bcl = new IBeanClassLoader(blueprintNameVersion,mr,ccck,moduleDir,moduleTempDir);
							synchronized(realBlueprintClassLoaders){
								realBlueprintClassLoaders.put(realNameVersion, new WeakReference<IBeanClassLoader>(bcl));
							}
							blueprintClassLoader = bcl;
						}
					}
				}
				synchronized(blueprintClassLoaders){
					blueprintClassLoaders.put(needNameVersion,new WeakReference<IBeanClassLoader>(blueprintClassLoader));
				}
			}
		}
		
		return blueprintClassLoader;
	}
	
}
